#include "chapter19.h"

int exercise19_1()
{
    int *temp = new int(1024);
    cout << *temp << "\n";
    delete temp;
    return 0;
}

int exercise19_2()
{
    int *t1 = new int(1024);
    char *test = new char('a');
    int *t2 = new int(2048);

    cout << t1 << "," << test << "," << t2 << "\n";
    delete t1;
    delete test;
    delete t2;
    // // I dont think it's necessary to redo strvec. Let's test allocate directly.
    std::allocator<int> alloc;
    int temp, n(10);
    int *const p = alloc.allocate(n);
    int *q = p;

    while (cin >> temp && (q != p + n))
    {
        cout << "Before construct: " << q << "\n";
        alloc.construct(q++, temp);
        // see is there conflict with alloc's memory.
        int *another = new int(100);
        cout << "another pointer :" << another << "\n";
        delete another;
    }

    while (q != p)
    {
        cout << *(--q) << " ";
        // 此处，destory函数只调用析构函数，并不会释放alloc预申请的内存，故我们自定义的delete没有被使用到。
        alloc.destroy(q);
    }
    // 此处释放了内存。
    alloc.deallocate(p, n);
    return 0;
}

int exercise19_3()
{
    AA *pa = new CC;
    if (BB *pb = dynamic_cast<BB *>(pa))
        cout << "B *pb = dynamic_cast<B *>(pa) is OK\n";
    else
        cout << "First case failed.\n";

    BB *pb = new BB;
    if (CC *pc = dynamic_cast<CC *>(pb))
        cout << "C *pc = dynamic_cast<C *>(pb) is OK\n";
    else
        cout << "Second case failed.\n";

    // base class A is ambiguous
    // A *pa = new D;
    // B *pb = dynamic_cast<B *>(pa);

    return 0;
}

int exercise19_4()
{
    AA *pa = new CC;
    cout << pa->get() << "\n";
    if (CC *pc = dynamic_cast<CC *>(pa))
    {
        cout << pc->get() << "\n";
    }
    else
    {
        cout << pa->get() << "\n";
    }
    return 0;
}

int exercise19_5()
{
    return 0;
}

int exercise19_6()
{
    return 0;
}

int exercise19_7()
{
    return 0;
}

int exercise19_8()
{
    return 0;
}

int exercise19_9()
{
    return 0;
}

int exercise19_10()
{
    // P1A
    // P1A
    // 1C
    AA *pa = new CC;
    cout << typeid(pa).name() << endl;
    CC cobj;
    AA &ra = cobj;
    cout << typeid(&ra).name() << endl;
    BB *px = new BB;
    ra = *px;
    cout << typeid(ra).name() << endl;
    return 0;
}

int exercise19_11()
{
    return 0;
}

int exercise19_12()
{
    auto pc = Screen::getCurosrPointer();

    Screen s1(10, 10, 2, 'a'), s2(5, 5, 20, 'c');

    cout << s1.*pc << "\n"
         << s2.*pc << "\n";

    return 0;
}

int exercise19_13()
{
    Sales_data s1("this is a temp", 10, 9.99), s2("another temp", 9, 10.01);
    auto pc = Sales_data::getBookNoPtr();

    cout << s1.*pc << "\n"
         << s2.*pc << "\n";
    return 0;
}

int exercise19_14()
{
    auto pmf = &Screen::get_cursor;

    Screen s1(10, 10, 2, 'a'), s2(5, 5, 20, 'c');

    cout << (s1.*pmf)() << "," << (s2.*pmf)() << "\n";

    pmf = &Screen::get;
    cout << (s1.*pmf)() << "," << (s2.*pmf)() << "\n";
    return 0;
}

int exercise19_15()
{
    return 0;
}

int exercise19_16()
{
    using avgPrice = double (Sales_data::*)() const;

    avgPrice fcn = &Sales_data::avg_price;

    Sales_data s1("this is a temp", 10, 9.99), s2("another temp", 9, 10.01);

    cout << (s1.*fcn)() << ", " << (s2.*fcn)() << "\n";
    return 0;
}

int exercise19_17()
{
    return 0;
}

int exercise19_18()
{
    vector<string> svec{"abc", "", "abedddd", "aassdfadsfas", "", "a", "", "hhh"};

    // method 1: using std::function
    std::function<bool(const string &)> fcn = &string::empty;
    auto result1 = std::count_if(svec.cbegin(), svec.cend(), fcn);
    cout << "method 1: using std::function 's result is " << result1 << "\n";

    // method 2: using std::mem_fn
    auto result2 = std::count_if(svec.cbegin(), svec.cend(), std::mem_fn(&string::empty));
    cout << "method 2: using std::mem_fn 's result is " << result2 << "\n";

    // method 3: using std::bind
    auto fcn2 = std::bind(&string::empty, std::placeholders::_1);
    auto result3 = std::count_if(svec.cbegin(), svec.cend(), fcn2);
    cout << "method 3: using std::bind 's result is " << result3 << "\n";

    return 0;
}

int exercise19_19()
{
    Sales_data s1("this is a temp", 10, 9.99), s2("another temp", 9, 10.01), s3("third temp", 9, 11.01);
    vector<Sales_data> svec{s1, s2, s3};

    // method 1: using std::bind
    double threshold(9.0);
    auto fcn = std::bind(&Sales_data::avg_price, std::placeholders::_1);
    auto result1 = std::find_if(svec.cbegin(), svec.cend(), [&](const Sales_data &s)
                                { return fcn(s) > threshold; });
    cout << "method 1: using std::bind 's result is " << result1->isbn() << "\n";

    // method 2: using std::function
    double threshold2(10.0);
    std::function<double(const Sales_data &)> fcn2 = &Sales_data::avg_price;
    auto result2 = std::find_if(svec.cbegin(), svec.cend(), [&](const Sales_data &s)
                                { return fcn2(s) > threshold2; });
    cout << "method 2: using std::function 's result is " << result2->isbn() << "\n";

    // method 3: using std::mem_fn
    double threshold3(11.0);
    auto fcn3 = std::mem_fn(&Sales_data::avg_price);
    auto result3 = std::find_if(svec.cbegin(), svec.cend(), [&](const Sales_data &s)
                                { return fcn3(s) > threshold3; });
    cout << "method 2: using std::function 's result is " << result3->isbn() << "\n";

    return 0;
}

int exercise19_20()
{
    std::ifstream inFile("/home/tian/projects/hellocpp/src/CppPrimer5th/chapter12/data/input_text.txt");
    if (inFile.is_open())
    {
        // infile is an ifstream that is the file we want to query
        TextQueryNested tq(inFile); // store the file and build the query map
        // // iterate with the user: prompt for a word to find and print results
        while (true)
        {
            cout << "enter word to look for, or q to quit: ";

            string s;
            // stop if we hit end-of-file on the input or if a 'q' is entered
            if (!(cin >> s) || s == "q")
                break;
            // run the query and print the results
            try
            {
                print(cout, tq.query(s)) << endl;
            }
            catch (std::runtime_error e)
            {
                cout << "# ERR: Exception in " << __FILE__;
                cout << "(" << __FUNCTION__ << ") on line "
                     << __LINE__ << endl;
                cout << "# ERR: " << e.what();
                break;
            }
        }
    }
    return 0;
}

void Token::copyUnion(const Token &t)
{
    switch (t.tok)
    {
    case Token::INT:
        ival = t.ival;
        break;
    case Token::CHAR:
        cval = t.cval;
        break;
    case Token::DBL:
        dval = t.dval;
        break;
    case Token::STR:
        new (&sval) string(t.sval);
        break;
    case Token::SDATA:
        new (&sdval) Sales_data(t.sdval);
        break;
    }
}

void Token::moveUnion(Token &&t)
{
    cout << "using moveUnion\n";

    switch (t.tok)
    {
    case Token::INT:
        ival = t.ival;
        break;
    case Token::CHAR:
        cval = t.cval;
        break;
    case Token::DBL:
        dval = t.dval;
        break;
    case Token::STR:
        new (&sval) string(std::move(t.sval));
        break;
    case Token::SDATA:
        // sales_data does not have move operations.
        new (&sdval) Sales_data(t.sdval);
        break;
    }
}

Token &Token::operator=(const Token &t)
{
    if (tok == STR && t.tok != STR)
        sval.~string();
    if (tok == SDATA && t.tok != SDATA)
        sdval.~Sales_data();

    if (tok == STR && t.tok == STR)
        sval = t.sval;
    else if (tok == SDATA && t.tok == SDATA)
        sdval = t.sdval;
    else
        copyUnion(t);
    tok = t.tok;
    return *this;
}

Token &Token::operator=(Token &&t)
{
    if (tok == STR && t.tok != STR)
        sval.~string();
    if (tok == SDATA && t.tok != SDATA)
        sdval.~Sales_data();

    if (tok == STR && t.tok == STR)
        sval = t.sval;
    else if (tok == SDATA && t.tok == SDATA)
        sdval = t.sdval;
    else
        moveUnion(std::move(t));
    tok = t.tok;
    return *this;
}

Token &Token::operator=(const std::string &str)
{
    if (tok == SDATA)
        sdval.~Sales_data();
    if (tok == STR)
        sval = str;
    else
        new (&sval) string(str);
    tok = STR;
    return *this;
}

Token &Token::operator=(const Sales_data &sd)
{
    if (tok == STR)
        sval.~string();
    if (tok == SDATA)
        sdval = sd;
    else
        new (&sdval) Sales_data(sd);
    tok = SDATA;
    return *this;
}

Token &Token::operator=(char c)
{
    if (tok == STR)
        sval.~string();
    if (tok == SDATA)
        sdval.~Sales_data();
    cval = c;
    tok = CHAR;
    return *this;
}

Token &Token::operator=(int i)
{
    if (tok == STR)
        sval.~string();
    if (tok == SDATA)
        sdval.~Sales_data();
    ival = i;
    tok = INT;
    return *this;
}

Token &Token::operator=(double d)
{
    if (tok == STR)
        sval.~string();
    if (tok == SDATA)
        sdval.~Sales_data();
    dval = d;
    tok = DBL;
    return *this;
}

int exercise19_21()
{
    Token a, b;
    a = "temp string";

    cout << "a : " << a << "\n";

    b = std::move(a);

    cout << "b : " << b << "\n";

    Token c(std::move(b));

    cout << "c : " << c << "\n";
    return 0;
}

int exercise19_22()
{

    // a : this is a temp
    // using moveUnion
    // b : this is a temp
    // using move constructor
    // using moveUnion
    // c : this is a temp
    // change c to STR, c : temp string
    // using move constructor
    // using moveUnion
    // d : temp string
    Sales_data s1("this is a temp", 10, 9.99), s2("another temp", 9, 10.01);

    Token a, b;
    a = s1;
    cout << "a : " << a << "\n";

    b = std::move(a);
    cout << "b : " << b << "\n";

    Token c(std::move(b));
    cout << "c : " << c << "\n";

    c = "temp string";
    cout << "change c to STR, c : " << c << "\n";

    Token d(std::move(c));
    cout << "d : " << d << "\n";

    d = 20.222;
    cout << "change d to DBL, d : " << d << "\n";

    d = s2;
    cout << "change d to SDDATA, d : " << d << "\n";
    return 0;
}

int exercise19_23()
{
    return 0;
}

int exercise19_24()
{
    Sales_data s1("this is a temp", 10, 9.99);

    Token a;
    a = s1;
    cout << "a : " << a << "\n";
    a = a;
    cout << "a : " << a << "\n";
    return 0;
}

int exercise19_25()
{
    return 0;
}

int exercise19_26()
{
    return 0;
}
